using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace InkRun
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        PlayerFactory pFactory;
        InputHandler ihandler;
        Texture2D bgPaper;
        Tiler tiler;
        List<Texture2D> tileSprites;
        List<ScrollingLevel> levelTiles;
        InkWave inkWave;
        int currentTile = 0;
        int nextTile = 0;
        List<Obstacle> obstacles;
        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            // TODO: Add your initialization logic here
            pFactory = PlayerFactory.Instance();
            ihandler = new InputHandler(this);
            this.graphics.PreferredBackBufferHeight = (int)GameParameters.SCREEN_SIZE.Y;
            this.graphics.PreferredBackBufferWidth = (int)GameParameters.SCREEN_SIZE.X;
            obstacles = new List<Obstacle>();
            tiler = new Tiler();
            graphics.ApplyChanges();
            base.Initialize();
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);
            bgPaper = Content.Load<Texture2D>("Sprites/notebook_paper");
            levelTiles = new List<ScrollingLevel>();
            tiler.LoadTiles(Content);
            InkWave.InkWaveSprite = Content.Load<Texture2D>("Sprites/InkWall");
            inkWave = new InkWave(new Vector2(InkWave.InkWaveSprite.Bounds.Height / 2, InkWave.InkWaveSprite.Bounds.Width / 2));
            Obstacle.obstacleSprite = Content.Load<Texture2D>("Sprites/Obstacle1");
            Player.SplodgeSprite = Content.Load<Texture2D>("Sprites/Splodge1");
            pFactory.LoadContent(Content);
            // TODO: use this.Content to load your game content here
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }
        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            // Allows the game to exit
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                this.Exit();

            // TODO: Add your update logic here
            UpdateObstacles(gameTime);
            ihandler.Update(gameTime);
            inkWave.Update(gameTime);
            tiler.Update(gameTime);
            if (pFactory.Players.Count() == 0)
            {
                pFactory.CreatePlayers();
            }
            pFactory.Update(gameTime, tiler);
            
            CheckCollisions();
            if (tiler.tilerState == Tiler.TilerState.ScrollingRightToLeft)
            {
                inkWave.velocity.X = PlayerFactory.Instance().GetFurthestForwardPlayer().GetMaxSpeed() * -1;
            }
            else
            {
                inkWave.velocity.X = 2.0f;
            }
            base.Update(gameTime);
        }
        public void UpdateObstacles(GameTime gameTime)
        {
            Random r = new Random(gameTime.TotalGameTime.Milliseconds);
            for (int i = 0; i < obstacles.Count(); i++)
            {
                obstacles[i].Update(gameTime);
                if (!obstacles[i].Alive)
                {
                    obstacles.Remove(obstacles[i]);
                }
            }
            if (obstacles.Count() < 1)
            {
                Obstacle ob = new Obstacle(new Vector2(GameParameters.SCREEN_SIZE.X, r.Next(0,(int)GameParameters.SCREEN_SIZE.Y)));
                obstacles.Add(ob);
            }

        }
        public void CheckCollisions()
        {
            for (int i = 0; i < pFactory.Players.Count(); i++)
            {
                    if (CheckForCollision(pFactory.Players[i]))
                    {
                        if (pFactory.Players[i].speedDampener > 0.1f)
                        {
                            pFactory.Players[i].speedDampener -= 0.01f;
                        }
                    }
            }
        }
        public float GetDistanceBetweenPlayerAndTing(Player p, GameObject obj)
        {
            return Vector2.Distance(p.position, obj.position);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="gameObject"></param>
        /// <returns>True if collided</returns>
        public bool CheckForCollision(Player p)
        {
            // Need to take  into account the enemy scale
            if (TouchingBlackPixel(p))
            {
                //Alive = false;
                p.color = Color.White;
                return true;
            }
            //Alive = true;
            p.color = Color.Black;
            return false;
        }
        public bool TouchingBlackPixel(Player p)
        {
            for (int i = 0; i < pFactory.Players.Count(); i++)
            {                
                int sourceX = (int)((p.position.X - (p.sizeInPixels.X*0.8) / 2) - (p.tileCurrentlyOn.position.X - p.tileCurrentlyOn.sizeInPixels.X / 2));
                if (sourceX < 0)
                {
                    sourceX = 0;
                }

                int sourceY = (int)((p.position.Y - (p.sizeInPixels.Y*0.8) / 2) - (p.tileCurrentlyOn.position.Y - p.tileCurrentlyOn.sizeInPixels.Y / 2));
                if (sourceY < 0)
                {
                    sourceY = 0;
                }

                int sizeX = (int)(p.sizeInPixels.X*0.8);
                if (sourceX + sizeX >= (int)p.tileCurrentlyOn.sizeInPixels.X)
                {
                    sizeX = (int)p.tileCurrentlyOn.sizeInPixels.X - sourceX;
                }
                if (sizeX <= 0)
                {
                    // If last tile, don't go off edge.
                    //TODO: Hadle this elsewhere and more elegantly
                    return false;
                }


                Rectangle sourceRectangle =
                    new Rectangle(sourceX,sourceY, (int)sizeX , (int)p.sizeInPixels.Y);

                Color[] retrievedColor = new Color[(int)(sizeX * p.sizeInPixels.Y)];

                p.tileCurrentlyOn.sprite.GetData<Color>(0,sourceRectangle,retrievedColor, 0, retrievedColor.Count());

                foreach (Color color in retrievedColor)
                {
                    if (color.A > 0)
                    {
                        return true;
                    }
                }
            }
            return false;
        }
        /// <summary>
        /// This is called when the game should draw itself.lol
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);

            // TODO: Add your drawing code here
            spriteBatch.Begin(SpriteSortMode.Immediate,BlendState.AlphaBlend);
            spriteBatch.Draw(bgPaper, new Vector2(0, 0), Color.White);
            tiler.Draw(gameTime, spriteBatch);
            //for (int i = 0; i < obstacles.Count(); i++)
            //{
            //    obstacles[i].Draw(gameTime, spriteBatch);
            //}
            pFactory.Draw(gameTime, spriteBatch);
            inkWave.Draw(gameTime, spriteBatch);
            //spriteBatch.Draw(inkWave, new Vector2(inkWave.Bounds.Height/2, inkWave.Bounds.Width/2), null, Color.White, 0, new Vector2(inkWave.Bounds.Height / 2, inkWave.Bounds.Width / 2), 1, SpriteEffects.None, 0);
            
            spriteBatch.End();
            base.Draw(gameTime);
        }
    }
}
